#!/usr/bin/python

# Copyright 2008 John Lee <john.cylee@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see
# <http://www.gnu.org/licenses/>.


__author__ = "john.cylee@gmail.com"


import sys
import optparse
from xml.sax import make_parser, handler
from datetime import datetime

from gdata import blogger
from gdata.blogger import service
import atom


class BloggerClient(object):

    def __init__(self, email, password, blog_id):
        self.service = service.BloggerService(email=email, password=password)
        self.service.ProgrammaticLogin()
        self.blog_id = blog_id

    def createpost(self, title, content, published, updated, tags):
        entry = blogger.BlogPostEntry()
        entry.title = atom.Title(title_type='xhtml', text=title)
        entry.content = atom.Content(content_type='html', text=content)
        entry.published = atom.Published(text=published)
        entry.updated = atom.Updated(text=updated)
        for tag in tags:
            entry.category.append(atom.Category( \
                    scheme='http://www.blogger.com/atom/ns#', term=tag))
        entry = self.service.AddPost(entry, blog_id=self.blog_id)
        return entry.GetPostId()

    def createcomments(self, post_id, author_name, author_email, author_uri,
                       content, updated):
        entry = blogger.CommentEntry()
        author = atom.Author()
        if author_name != '':
            author.name = atom.Name(text=author_name)
        if author_email != '':
            author.email = atom.Email(text=author_email)
        if author_uri != '':
            author.uri = atom.Uri(text=author_uri)
        entry.author = author
        entry.content = atom.Content(content_type='html', text=content)
        entry.updated = atom.Updated(text=updated)
        entry = self.service.AddComment(entry, blog_id=self.blog_id, post_id=post_id)
        return entry.GetCommentId()


blogger_client = None


def wp_datetime_to_blogger(wp_datetime):
    dt = datetime.strptime(wp_datetime, '%Y-%m-%d %H:%M:%S')
    return dt.strftime('%Y-%m-%dT%H:%M:%S.000+00:00')


def create_post(wp_data):
    s = wp_datetime_to_blogger(wp_data['wp:post_date_gmt'])
    post_id = blogger_client.createpost(
        wp_data['title'], wp_data['content:encoded'], s, s, wp_data['categories'])
    for comment in wp_data['comments']:
        # don't care spam or pingback
        if comment['wp:comment_approved'] != '1' or comment['wp:comment_type'] == 'pingback':
            continue
        s = wp_datetime_to_blogger(comment['wp:comment_date_gmt'])
        blogger_client.createcomments(
            post_id, comment['wp:comment_author'], comment['wp:comment_author_email'],
            comment['wp:comment_author_url'], comment['wp:comment_content'], s)


class DecorationHandler(object):

    def __init__(self, parent):
        self._parent = parent

    def startElement(self, name, attrs):
        self._parent.startElement(name, attrs)

    def endElement(self, name, data):
        self._parent.endElement(name, data)


class CommentHandler(DecorationHandler):

    def __init__(self, parent):
        self._dict = {}
        super(CommentHandler, self).__init__(parent)

    def startElement(self, name, attrs):
        super(CommentHandler, self).startElement(name, attrs)
        return self

    def endElement(self, name, data):
        ret = self
        if name in ['wp:comment_id', 'wp:comment_author',
                    'wp:comment_author_email', 'wp:comment_author_url',
                    'wp:comment_author_IP', 'wp:comment_date',
                    'wp:comment_date_gmt', 'wp:comment_content',
                    'wp:comment_approved', 'wp:comment_type',
                    'wp:comment_parent']:
            self._dict[name] = data
        elif name == 'wp:comment':
            ret = self._parent
        super(CommentHandler, self).endElement(name, data)
        return ret

    @property
    def comment(self):
        return self._dict


class ItemHandler(DecorationHandler):

    def __init__(self, parent):
        self._dict = {}
        super(ItemHandler, self).__init__(parent)

    def startElement(self, name, attrs):
        ret = self
        if name == 'wp:comment':
            ret = self._comment = CommentHandler(self)
        super(ItemHandler, self).startElement(name, attrs)
        return ret

    def endElement(self, name, data):
        ret = self
        if name in ['title', 'link', 'pubDate', 'dc:creator', 'guid',
                    'description', 'content:encoded', 'wp:post_id',
                    'wp:post_date', 'wp:post_date_gmt', 'wp:comment_status',
                    'wp:ping_status', 'wp:post_name', 'wp:status',
                    'wp:post_parent', 'wp:menu_order', 'wp:post_type']:
            self._dict[name] = data
        elif name == 'category':
            self._dict.setdefault('categories', []).append(data)
        elif name == 'wp:comment':
            self._dict.setdefault('comments', []).append(self._comment.comment)
        elif name == 'item':
            create_post(self._dict)
            ret = self._parent
        super(ItemHandler, self).endElement(name, data)
        return ret


class CategoryHandler(DecorationHandler):
    """FIXME: find out the logic of 'parent'.
    """

    _forest = {}

    def startElement(self, name, attrs):
        return self

    def endElement(self, name, data):
        if name == 'wp:category':
            cat = CategoryHandler._forest.setdefault(self._name, {})
            cat['nicename'] = self._nicename
            if self._category_parent != '':
                p = CategoryHandler._forest.setdefault(self._category_parent, {})
                p.setdefault('children', []).append(self._name)
            return self._parent
        if name == 'wp:category_nicename':
            self._nicename = data
        elif name == 'wp:category_parent':
            self._category_parent = data
        elif name == 'wp:cat_name':
            self._name = data
        return self


class MainHandler(DecorationHandler):

    def startElement(self, name, attrs):
        if name == 'wp:category':
            return CategoryHandler(self)
        elif name == 'item':
            return ItemHandler(self)
        return self

    def endElement(self, name, data):
        return self


class WXRHandler(handler.ContentHandler, object):

    def __init__(self):
        super(WXRHandler, self).__init__()
        self._stack = []
        self._cdata = ''
        self._category = []
        self._handler = MainHandler(self)

    def startElement(self, name, attrs):
        self._stack.append(name)
        #print 'start stack: %s' % self._stack
        self._handler = \
            self._handler.startElement(name, attrs)
        self._cdata = ''

    def endElement(self, name):
        if self._stack[-1] != name:
            raise Exception('unmatched: %s' % name)
        self._stack.pop()
        self._handler = \
            self._handler.endElement(name, self._cdata)
        self._cdata = ''

    def characters(self, content):
        self._cdata += content.encode('utf-8')


def wxr_parser(file):
    parser = make_parser()
    parser.setContentHandler(WXRHandler())
    parser.parse(file)


def main():
    parser = optparse.OptionParser()
    parser.add_option('-n', '--username', type='string', dest='username',
                      help='Your blogger account')
    parser.add_option('-p', '--password', type='string', dest='password',
                      help='Your blogger password')
    parser.add_option('-b', '--blogid', type='string', dest='blog_id',
                      help='Your blog id')
    (options, args) = parser.parse_args()
    if len(args) == 0:
        raise Exception('Please specify the WXR xml file')
    globals()['blogger_client'] = \
        BloggerClient(options.username, options.password, options.blog_id)
    wxr_parser(args[0])


if __name__ == '__main__':
    main()
